﻿using System;
using System.Text;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.Client;
using System.Net.Mail;
using System.Net;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;


namespace TFSWorkItemAssigned
{
    public class AssignedToHandler:ISubscriber
    {
        public string sServerURL = "http://tfsservernamehere:8080/tfs/web/wi.aspx?id=";
        public string sCCUser = string.Empty;
        public string sAdminUser = "adminemail";
        public string sEmailServer = "smtpserver";
        public string sUserLookup = "";

        [Serializable()] 
        public class EmailLookup
        {
            public string sName = string.Empty;
            public string sEmail = string.Empty;
            
            public EmailLookup()
            { }

            public EmailLookup(string Name, string Email)
            {
                this.sName = Name;
                this.sEmail = Email;
            }

        }

        public AssignedToHandler()
        {
            System.Diagnostics.Trace.WriteLine("Creating assign to handler....");

        }

        /// <summary>
        /// Handle the process event
        /// </summary>
        public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs, out int statusCode, out string statusMessage, out Microsoft.TeamFoundation.Common.ExceptionPropertyCollection properties)
        {
            statusCode = 0;
            properties = null;
            statusMessage = String.Empty;
            int iID = 0;
            string sNewUser = string.Empty;
            string sOldUser = string.Empty;
            string sSummary = string.Empty;
            string sType = string.Empty;
            
            System.Diagnostics.Trace.WriteLine("Running process event for assign to handler");
 	        try
            {  
                if ((notificationType == NotificationType.Notification) && (notificationEventArgs is WorkItemChangedEvent))
                {
                    WorkItemChangedEvent data = notificationEventArgs as WorkItemChangedEvent;

                                     
                    iID = data.CoreFields.IntegerFields[0].NewValue;
                    sType = data.CoreFields.StringFields[0].NewValue;
                    sSummary = data.CoreFields.StringFields[1].NewValue;
                    if (sSummary == null) sSummary = "";
                    System.Diagnostics.Trace.WriteLine("Handler evaluating a " + sType.ToUpper());

                    
                    //Get the assigned to data
                    sNewUser = data.CoreFields.StringFields[5].NewValue.ToString();
                    sOldUser = data.CoreFields.StringFields[5].OldValue.ToString();
                    
                    //Debug data lines
                    //for (int i = 0; i < data.CoreFields.StringFields.GetLength(0); i++)
                    //{
                    //    System.Diagnostics.Trace.WriteLine(i.ToString() + " " + data.CoreFields.StringFields[i].Name + " " + data.CoreFields.StringFields[i].NewValue);
                    //}

                    //Now process the change
                    ProcessChange(iID, sType, sOldUser, sNewUser, sSummary);

                }
             } 
             catch (Exception ex)
             {
                    System.Diagnostics.Trace.WriteLine("Error " + ex.ToString());
             }
            
            return EventNotificationStatus.ActionPermitted;
        }

        /// <summary>
        /// Process the change events
        /// </summary>
        // <param name="sTitle">Title</param>
        public void ProcessChange(int iID, string sType, string sOldUser, string sNewUser, string sSummary)
        {
            StringBuilder sbMessage = null;
            StringBuilder sbSubject = null;
            StringBuilder sbDetail = null;
            bool bFound = false;
            System.Collections.Generic.List<EmailLookup> listEmails = null;         
           

            //Handle the ITS types
            if (sType.ToUpper() == "BUG" || sType.ToUpper() == "USER STORY" || sType.ToUpper() == "TASK" )
                {
                    System.Diagnostics.Trace.WriteLine("This is an '" + sType + "'");

                    //Load up the lookup list
                    listEmails = CreateLookupList(); 

                    if (sOldUser != sNewUser)
                    {
                        System.Diagnostics.Trace.WriteLine("Assignment has changed to " + sNewUser);

                        foreach (EmailLookup clLookup in listEmails)
                        {
                            if (clLookup.sName.ToUpper().Trim() == sNewUser.ToUpper().Trim())
                            {
                                //Format the subject line
                                sbSubject = new StringBuilder();
                                sbSubject.Append("A ");
                                sbSubject.Append(sType.ToLower());
                                sbSubject.Append(" has assigned to you - ");
                                sbSubject.Append(sType.ToLower());
                                sbSubject.Append(" # " + iID.ToString());    

                                //See if you can get the detail for this item
                                sbDetail = new StringBuilder();
                                sbDetail.Append(this.ItemDetailsGet(sType, iID));

                                //Format the message text
                                sbMessage = new StringBuilder();
                                sbMessage.Append(sNewUser + ", <br/><br/> The following " + sType.ToLower() + " has been assigned to you (");
                                sbMessage.Append(sType + " #" + iID.ToString());
                                sbMessage.Append("). " + Environment.NewLine);
                                sbMessage.Append(" <a href=\"" + sServerURL + iID.ToString() + "\">[view item in browser]</a><br/> ");
                                if (sbDetail.Length > 0) sbMessage.Append(sbDetail.ToString() + Environment.NewLine);
                                sbMessage.Append("To see more information on this following item please click on the following link <a href=\"" + sServerURL + iID.ToString() + "\"> " + sType.ToLower() + " detail</a> to bring up in your browser.<br/>");
                                sbMessage.Append("<br/>Your TFS System");                          

                                //Send the e-mail
                                SendEmail("assignmentemail", clLookup.sEmail, sbSubject.ToString(), sbMessage.ToString(), sCCUser);
                                bFound = true;
                                break;
                            }
                        }

                        if (!bFound)
                        {
                            //Format the message line
                            sbMessage = new StringBuilder();
                            sbMessage.Append(sNewUser + " was not found to e-mail " + sType + "-" + sType + "#" + iID.ToString());

                            //Format the subject line
                            sbSubject = new StringBuilder();
                            sbSubject.Append(sNewUser + " was not found to e-mail " + sType + "-" + sType + "#" + iID.ToString());

                            //Send the e-mail
                            SendEmail("assignmentemail", sAdminUser, sbSubject.ToString(), sbMessage.ToString(), sCCUser);
                        }
                    }   
                    else
                    {
                        System.Diagnostics.Trace.WriteLine("Assignment has NOT changed from " + sOldUser);
                    }
                }
        }

        /// <summary>
        /// If the tfs item can be loaded get additional detail for the email
        /// </summary>
        /// <param name="sType">Type of workitem</param>
        /// <param name="iWorkItem">ID of workitem</param>
        public string ItemDetailsGet(string sType, int iWorkItem)
        {
            StringBuilder sbDetailText = new StringBuilder();
            TfsTeamProjectCollection clProjectCollection = null;
            Uri clURL = null;
            NetworkCredential clID = null;
            StringBuilder clSb = new StringBuilder();
            Revision clHistoryRevision = null;
            string sSeparator = "<br/>";
            string sSeparatorLine = "<hr/>";
            string sDetail = string.Empty;
            WorkItemStore clWorkItemStore = null;
            WorkItem clWorkItem = null;

            //Connect to the tfs system
            System.Diagnostics.Trace.WriteLine("Getting detail for item " + iWorkItem.ToString() + " from TFS");

            try
            {
                if (System.Environment.MachineName != "TFSSERVERNAME")
                {
                    clURL = new Uri("http://tfsservername:8080/tfs/defaultcollection");
                    clID = new NetworkCredential("username", "password", "TFSSERVERNAME");
                }
                else
                {
                    //If you install this on the server it will not connect due to
                    //windows permission. Connect locally and you will not have the issue
                    clURL = new Uri("http://localhost:8080/tfs/defaultcollection");
                    clID = new NetworkCredential("username", "password", "");
                }
                
                clProjectCollection = new TfsTeamProjectCollection(clURL, clID);
                clProjectCollection.EnsureAuthenticated();
                clWorkItemStore = clProjectCollection.GetService<WorkItemStore>();
                clWorkItem = clWorkItemStore.GetWorkItem(iWorkItem);

                // Get latest revision history
                clHistoryRevision = clWorkItem.Revisions[clWorkItem.Revisions.Count - 1];
                if (clHistoryRevision != null && clHistoryRevision.Fields["History"].Value.ToString().Length > 0)
                {
                    //Last note
                    clSb.Append(sSeparatorLine);
                    clSb.Append("<B>Latest History Note:</B> ");
                    clSb.Append(sSeparator + sSeparator);
                    clSb.Append("<B><I><FONT COLOR='RED'>");
                    clSb.Append(clHistoryRevision.Fields["History"].Value + sSeparator);
                    clSb.Append("</I></B></FONT>");
                    clSb.Append(sSeparator);
                    clSb.Append(sSeparatorLine);
                }

                clSb.Append(sSeparator);
                switch (sType.ToUpper())
                {
                    //Handle the development items
                    case "BUG":
                        clSb.Append(FormatSection("Summary", "Title" , clWorkItem));
                        clSb.Append(FormatSection("Detail", "Repro Steps", clWorkItem));
                        clSb.Append(FormatSection("Severity", "Severity", clWorkItem));
                        clSb.Append(FormatSection("Created By", "Created By", clWorkItem));
                        //clSb.Append(FormatSection("Client Name", "Client Name", clWorkItem)); -- This was a custom field
                        break;
                    case "USER STORY":
                        clSb.Append(FormatSection("Summary", "Title" , clWorkItem));
                        clSb.Append(FormatSection("Functionality", "Functionality", clWorkItem));
                        clSb.Append(FormatSection("Severity", "Severity", clWorkItem));
                        clSb.Append(FormatSection("Created By", "Created By", clWorkItem));
                        //clSb.Append(FormatSection("Client Name", "Client Name", clWorkItem)); -- This was a custom field
                        break;
                    case "TASK":
                        clSb.Append(FormatSection("Summary", "Title" , clWorkItem));
                        clSb.Append(FormatSection("Detail", "Description", clWorkItem));
                        clSb.Append(FormatSection("Severity", "Severity", clWorkItem));
                        clSb.Append(FormatSection("Created By", "Created By", clWorkItem));
                        //clSb.Append(FormatSection("Client Name", "Client Name", clWorkItem)); -- This was a custom field
                        break;
                    default:
                        break;
                }
                clSb.Append(sSeparator);
                
                sbDetailText.Append(clSb.ToString());
            }
            catch
            {
                //Connect to the tfs system
                System.Diagnostics.Trace.WriteLine("Unable to connect and get item information from TFS");
            }

            return sbDetailText.ToString();
        }

        /// <summary>
        /// Helper function that helps format e-mail line from workitem
        /// </summary>
        /// <param name="sTitle">Title</param>
        /// <param name="sKey">Field Key</param>
        /// <param name="clWorkItem">Workitem object</param>
        private string FormatSection(string sTitle, string sKey, WorkItem clWorkItem)
        {
            StringBuilder sbReturn = new StringBuilder();
            string sSeparator = "<br/>";
            string sSeparatorLine = "<hr/>";
            
            try
            {
                sbReturn.Append("<B>" + sTitle + ":</B> ");
                sbReturn.Append(sSeparatorLine);
                sbReturn.Append(clWorkItem.Fields[sKey].Value.ToString());
                sbReturn.Append(sSeparator + sSeparator);
                //System.Diagnostics.Trace.WriteLine(sbReturn.ToString());
            }
            catch
            {
                System.Diagnostics.Trace.WriteLine("Unable to return field " + sKey);
            }

            return sbReturn.ToString();
        }
        public Type[]  SubscribedTypes()
        {
            return new Type[] { new WorkItemChangedEvent().GetType()};
        }

        public string  Name
        {
	        get { return "TFS Emailer Assign Handler"; }
        }

        public SubscriberPriority  Priority
        {
	        get { return SubscriberPriority.Normal; }
        }

        /// <summary>
        /// Load a lookup list for resolving the e-mail address from the username user in TFS
        /// -- This had to be done because we used TFS in workstation mode
        /// </summary>
        public System.Collections.Generic.List<EmailLookup> CreateLookupList()
        { 
            System.Collections.Generic.List<EmailLookup> listLookup = new System.Collections.Generic.List<EmailLookup>();
            XmlNodeList listNodes = null;
            XmlNode clNode = null;
            XmlDocument xdoc = new XmlDocument();
            
            try
            {


                string currentAssemblyDirectoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);

                xdoc.Load(currentAssemblyDirectoryName + "\\UserList.xml");
                clNode = xdoc.SelectSingleNode("ArrayOfEmailLookup");
                listNodes = clNode.SelectNodes("EmailLookup");

                foreach (XmlNode clItem in listNodes)
                {
                    listLookup.Add(new EmailLookup(clItem.SelectSingleNode("sName").InnerText, clItem.SelectSingleNode("sEmail").InnerText));
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("Loading of user list failed with error: " + ex.ToString());
            }

            return listLookup;
        }

        //public String SerializeObject(object oObj)
        //{
        //    string sReturn = string.Empty;
        //    System.IO.StringWriter txtXML;

        //    System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(oObj.GetType());
        //    txtXML = new System.IO.StringWriter();
        //    x.Serialize(txtXML, oObj);
        //    sReturn = txtXML.ToString();
        //    txtXML.Close();

        //    return sReturn;
        //}

        /// <summary>
        /// Function that sends out an e-mail
        /// </summary>
        /// <param name="sFrom">Email from</param>
        /// <param name="sTo">Email address to send to</param>
        /// <param name="sSubject">Subject line</param>
        /// <param name="sBody">Body of e-mail</param>
        /// <param name="sCC">Carbon Copy Email Address</param>
        public string SendEmail(string sFrom, string sTo, string sSubject, string sBody, string sCC)
        {
            string sendResult = "email sent";
            try
            {
                System.Diagnostics.Trace.WriteLine("Sending email to " + sTo); 

                SmtpClient clMainClient = new SmtpClient(sEmailServer, Convert.ToInt32(587));
                MailAddress clFrom = new MailAddress(sFrom, "TFS Emailer Admin Robot", Encoding.UTF8);
                MailAddress clTo = new MailAddress(sTo);
                MailAddress clCC = null;
                
                MailMessage clMessage = new MailMessage(clFrom, clTo);
                clMessage.Body = sBody;
                clMessage.BodyEncoding = Encoding.UTF8;
                clMessage.IsBodyHtml = true;
                clMessage.Subject = sSubject;
                clMessage.SubjectEncoding = Encoding.UTF8;

                if (sCC.Length > 0)
                {
                    clCC = new MailAddress(sCC);
                    clMessage.CC.Add(clCC);
                }

                clMainClient.Credentials = new NetworkCredential("smtpusername", "smptuserpassword");
                clMainClient.Send(clMessage);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("Error " + e.ToString());
                sendResult = e.Message;
            }
            return sendResult;
        }


}

}
